#!usr/bin/env python
# -*- coding:utf-8 _*-
"""
@author:Zx
@file: app.py.py
@time: 2025/3/21  09:55
# @describe: 旧版本-功能未拆分(废弃)
"""
import logging
import os
import uuid

from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, session
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
from pathlib import Path
from flask import session, make_response
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
import random
import string

import json
from datetime import datetime, timedelta, date
from logging_config import configure_logging
from werkzeug.security import generate_password_hash, check_password_hash


app = Flask(__name__)
app.secret_key = '2-4V/QWe9tn4'

# 配置日志
configure_logging(app)

# 获取日志器
logger = logging.getLogger(__name__)


# 设置图片保存目录
UPLOAD_FOLDER = 'static/images'
if not os.path.exists(UPLOAD_FOLDER):
    os.makedirs(UPLOAD_FOLDER)


# 设置登录管理
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'


# 确保data目录存在
DATA_DIR = Path("data")
DATA_DIR.mkdir(exist_ok=True)

# 统计文件路径
STATS_FILE = DATA_DIR / 'stats.json'
USER_FILE = DATA_DIR / 'users.json'


# 添加模板过滤器
@app.template_filter('number_format')
def number_format(value):
    try:
        value = int(value)
        if value >= 10000:
            return f"{value/10000:.1f}w"  # 例如：1.2w
        elif value >= 1000:
            return f"{value/1000:.1f}k"   # 例如：1.5k
        return str(value)
    except:
        return str(value)


def init_stats_file():
    """初始化统计文件"""
    try:
        if not STATS_FILE.exists():
            with open(STATS_FILE, 'w', encoding='utf-8') as f:
                json.dump({
                    "total_visits": 0,
                    "daily_visits": {},
                    "last_visit_date": None
                }, f, ensure_ascii=False, indent=2)
    except Exception as e:
        logger.error(f'初始化统计文件-失败: {str(e)}', exc_info=True)


def init_user_file():
    """初始化用户文件"""
    try:
        if not USER_FILE.exists():
            with open(USER_FILE, 'w', encoding='utf-8') as f:
                json.dump([], f, ensure_ascii=False, indent=2)
    except Exception as e:
        logger.error(f'初始化用户文件-失败: {str(e)}', exc_info=True)


def update_visit_stats():
    """更新访问统计"""
    try:
        # 读取现有统计
        with open(STATS_FILE, 'r', encoding='utf-8') as f:
            stats = json.load(f)

        # 获取当前日期
        today = datetime.now().strftime('%Y-%m-%d')

        # 更新统计
        stats['total_visits'] += 1
        stats['daily_visits'][today] = stats['daily_visits'].get(today, 0) + 1
        stats['last_visit_date'] = today

        # 写回文件
        with open(STATS_FILE, 'w', encoding='utf-8') as f:
            json.dump(stats, f, ensure_ascii=False, indent=2)

        return stats['total_visits']
    except Exception as e:
        logger.error(f'更新访问统计-失败: {str(e)}', exc_info=True)


def get_user_count():
    """获取注册用户数"""
    try:
        with open(USER_FILE, 'r', encoding='utf-8') as f:
            users = json.load(f)
        return len(users)
    except Exception as e:
        logger.error(f'获取注册用户数-失败: {str(e)}', exc_info=True)


# 初始化文件
init_stats_file()
init_user_file()


def get_blocked_ips_file():
    """获取存储被限流IP的文件路径"""
    try:
        today = datetime.now().strftime("%Y-%m-%d")
        return DATA_DIR / f"blocked_ips_{today}.txt"
    except Exception as e:
        logger.error(f'获取存储被限流IP的文件路径-失败: {str(e)}', exc_info=True)


def record_blocked_ip(ip: str):
    """记录被限流的IP到文件中"""
    file_path = get_blocked_ips_file()
    timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    try:
        # 检查IP是否已经记录过
        if file_path.exists():
            with open(file_path, 'r') as f:
                existing_ips = [line.split(' - ')[0] for line in f.readlines()]
                if ip in existing_ips:
                    return

        # 记录新IP
        with open(file_path, 'a') as f:
            f.write(f"{ip} - {timestamp}\n")
        logger.info(f"记录被限流IP: {ip} 在 {timestamp}")
    except Exception as e:
        logger.error(f"记录被限流IP失败: {e}")


# 初始化限流器
limiter = Limiter(
    app=app,
    key_func=get_remote_address,
    default_limits=["200 per minute", "50 per 10 seconds"],  # 全局限制-每分钟最多 200 次请求; 每 10 秒最多 50 次请求。
    storage_uri="memory://",  # 使用内存存储限流计数
    on_breach=record_blocked_ip  # 触发限流时自动记录IP
)


# 自定义限流错误处理
@app.errorhandler(429)
def ratelimit_handler(e):
    try:
        ip = get_remote_address() or "unknown"  # 防止 None
    except Exception as e:
        ip = "unknown"
        logger.error(f"获取客户端IP失败：{e}")

    try:
        record_blocked_ip(ip)  # 确保失败不影响主逻辑
    except Exception as e:
        logger.error(f"记录IP失败: {e}")

    return jsonify({
        "error": "rate_limit_exceeded",
        "message": "请求过于频繁，请稍后再试",
        "detail": str(e.description),
        "client_ip": ip  # 可选：返回客户端IP（脱敏后）
    }), 429


@app.route('/example')
def example():
    try:
        logger.debug('这是一个调试信息')
        logger.info('处理请求开始')
        # 业务逻辑...
        logger.info('处理请求成功')
        return 'OK'
    except Exception as e:
        logger.error(f'处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 注册自定义过滤器
@app.template_filter('datetimeformat')
def datetimeformat(value, format='%Y-%m-%d %H:%M'):
    try:
        if isinstance(value, str):
            value = datetime.fromisoformat(value)  # 可能抛出 ValueError
        return value.strftime(format)
    except (ValueError, AttributeError) as e:
        logger.error(f"日期格式转换失败: {e}, 原始值: {value}")
        return str(value)  # 降级方案：返回原始字符串或默认值


# 加载数据
def load_data(filename):
    try:
        with open(f'data/{filename}', 'r', encoding='utf-8') as f:
            return json.load(f)
    except Exception as e:
        logger.error(f'加载数据-处理失败: {str(e)}', exc_info=True)
        return 'Error', 500


def save_data(data, filename):
    try:
        with open(f'data/{filename}', 'w', encoding='utf-8') as f:
            json.dump(data, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f'保存数据-处理失败: {str(e)}', exc_info=True)
        return 'Error', 500


def get_next_user_id():
    try:
        users = load_users()
        if not users:
            return 1
        max_id = max(user['id'] for user in users)
        return max_id + 1
    except Exception as e:
        logger.error(f"获取下一个的用户ID-失败: {e}")


# 用户类
class User(UserMixin):
    def __init__(self, user_data):
        self.id = user_data['id']
        self.username = user_data['username']
        self.email = user_data['email']
        self.password = user_data['password']
        self.role = user_data.get('role', 'user')  # 默认角色为user
        # 确保 data_add_records 存在，如果不存在则初始化为空字典
        self.data_add_records = user_data.get('data_add_records', {})

    def can_add_data(self):
        if self.role == 'admin':
            return True

        # 直接使用 get_today_add_count 方法保持一致性
        return self.get_today_add_count() < 10

    def get_today_add_count(self):
        if self.role == 'admin':
            return 0

        today = date.today().isoformat()
        # 使用 get 方法并提供默认值 0
        return self.data_add_records.get(today, 0)

    def record_data_add(self):
        if self.role != 'admin':
            today = date.today().isoformat()
            # 更安全的计数方式
            current_count = self.data_add_records.get(today, 0)
            self.data_add_records[today] = current_count + 1
            self._save_user_data()

    def _save_user_data(self):
        try:
            users = load_users()
            for i, user in enumerate(users):
                if user['id'] == self.id:
                    # 更新所有可能变更的字段
                    users[i] = {
                        'id': self.id,
                        'username': self.username,
                        'email': self.email,
                        'password': self.password,
                        'role': self.role,
                        'data_add_records': self.data_add_records
                    }
                    break

            save_users(users)
            return True
        except Exception as e:
            logger.error(f"保存用户数据失败: {e}")
            return False


@login_manager.user_loader
def load_user(user_id):
    try:
        users = load_users()
        user_data = next((u for u in users if u['id'] == int(user_id)), None)
        if user_data:
            # 确保有 data_add_records 字段，如果没有则初始化为空字典
            user_data.setdefault('data_add_records', {})
            return User(user_data)
    except Exception as e:
        logger.error(f"加载用户失败: {e}")
        return None


def load_users():
    try:
        with open('data/users.json', 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        logger.error("用户数据文件未找到: data/users.json")
        return []  # 返回空列表而不是None，避免调用方需要额外处理
    except json.JSONDecodeError as e:
        logger.error(f"用户数据文件格式错误: {e}")
        return []
    except Exception as e:
        logger.error(f"加载用户数据时发生未知错误: {e}")
        return []


def save_users(users):
    try:
        # 确保目录存在
        os.makedirs('data', exist_ok=True)

        # 使用原子写入模式 (Python 3.8+)
        file_path = Path('data/users.json')
        with open(file_path, 'w', encoding='utf-8') as f:
            json.dump(users, f, indent=2, ensure_ascii=False)

        # 验证写入是否成功
        if not file_path.exists():
            raise IOError("文件写入后验证失败")
    except (IOError, OSError) as e:
        logger.error(f"写入用户数据文件失败: {e}")
        raise RuntimeError("无法保存用户数据") from e
    except Exception as e:
        logger.error(f"保存用户数据时发生未知错误: {e}")
        raise RuntimeError("保存用户数据时发生错误") from e


# 登录路由
@app.route('/login', methods=['GET', 'POST'])
@limiter.limit("3 per second")  # 限流处理
def login():
    try:
        if current_user.is_authenticated:
            return redirect(url_for('index'))

        if request.method == 'POST':
            username = request.form.get('username')
            password = request.form.get('password')
            remember = True if request.form.get('remember') else False

            users = load_users()
            user = next((u for u in users if u['username'] == username), None)

            # 使用check_password_hash验证密码
            if user and check_password_hash(user['password'], password):
                # 更新最后登录时间
                user['last_login_time'] = datetime.utcnow().isoformat() + 'Z'
                user['last_activity_time'] = datetime.utcnow().isoformat() + 'Z'

                # 保存更新后的用户数据
                users = [u if u['username'] != username else user for u in users]
                save_users(users)

                # 登录用户
                user_obj = User(user)
                login_user(user_obj, remember=remember)

                flash('登录成功', 'success')
                return redirect(url_for('index'))
            else:
                flash('用户名或密码错误', 'error')

        return render_template('login.html')
    except Exception as e:
        logger.error(f'登录处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 登出路由
@app.route('/logout')
@limiter.limit("3 per second")  # 限流处理
@login_required
def logout():
    try:
        logout_user()
        return redirect(url_for('index'))
    except Exception as e:
        logger.error(f'登录处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


def generate_captcha():
    try:
        # 1. 生成验证码文本
        captcha_text = ''.join(random.choices(
            string.ascii_uppercase + string.digits,
            k=4
        ))

        # 2. 存储到session中（带超时设置）
        session['captcha'] = {
            'text': captcha_text,
            'expires_at': datetime.now() + timedelta(minutes=5)
        }

        # 3. 创建图片
        image = Image.new('RGB', (120, 40), color=(255, 255, 255))
        draw = ImageDraw.Draw(image)

        # 4. 加载字体（多字体回退机制）
        font = None
        font_paths = [
            'arial.ttf',
            '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',  # Linux常见路径
            '/System/Library/Fonts/SFNS.ttf',  # macOS
            'C:/Windows/Fonts/arial.ttf'  # Windows
        ]

        for path in font_paths:
            try:
                font = ImageFont.truetype(path, 24)
                break
            except (IOError, OSError):
                continue

        if font is None:
            font = ImageFont.load_default()
            logger.warning("使用默认字体，验证码可能不易识别")

        # 5. 绘制验证码文本（带扭曲效果）
        for i, char in enumerate(captcha_text):
            # 每个字符轻微随机偏移
            x = 10 + i * 28 + random.randint(-3, 3)
            y = 5 + random.randint(-5, 5)
            draw.text(
                (x, y),
                char,
                font=font,
                fill=(
                    random.randint(0, 150),
                    random.randint(0, 150),
                    random.randint(0, 150)
                )
            )

        # 6. 添加干扰元素
        # 干扰线
        for _ in range(5):
            draw.line(
                [
                    random.randint(0, 120),
                    random.randint(0, 40),
                    random.randint(0, 120),
                    random.randint(0, 40)
                ],
                fill=(
                    random.randint(100, 255),
                    random.randint(100, 255),
                    random.randint(100, 255)
                ),
                width=1
            )

        # 干扰点
        for _ in range(100):
            draw.point(
                [random.randint(0, 120), random.randint(0, 40)],
                fill=(
                    random.randint(150, 255),
                    random.randint(150, 255),
                    random.randint(150, 255)
                )
            )

        # 7. 扭曲变形（可选）
        # 可以使用PIL的ImageTransform进行更复杂的扭曲

        # 8. 输出图片
        buf = BytesIO()
        image.save(buf, 'png', quality=75)
        buf.seek(0)
        return buf

    except Exception as e:
        logger.error(f"生成验证码失败: {str(e)}", exc_info=True)
        # 返回一个错误图片或None，由调用方处理
        error_image = Image.new('RGB', (120, 40), color=(255, 255, 255))
        draw = ImageDraw.Draw(error_image)
        draw.text((10, 10), "ERROR", fill=(255, 0, 0))
        buf = BytesIO()
        error_image.save(buf, 'png')
        buf.seek(0)
        return buf


# 在视图函数中添加路由
@app.route('/register/captcha')
@limiter.limit("3 per second")  # 限流处理
def captcha():
    try:
        buf = generate_captcha()
        response = make_response(buf.read())
        response.headers['Content-Type'] = 'image/png'
        return response
    except Exception as e:
        logger.error(f'验证处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


@app.route('/register', methods=['GET', 'POST'])
@limiter.limit("3 per second")  # 限流处理
def register():
    try:
        if current_user.is_authenticated:
            return redirect(url_for('index'))

        if request.method == 'POST':
            # 验证验证码
            user_captcha = request.form.get('captcha', '').upper()
            username = request.form.get('username')
            email = request.form.get('email')
            password = request.form.get('password')
            confirm_password = request.form.get('confirm_password')

            # Basic validation
            if not all([username, email, password, confirm_password, user_captcha]):
                flash('请填写所有字段')
                return redirect(url_for('register'))

            if password != confirm_password:
                flash('两次输入的密码不一致')
                return redirect(url_for('register'))

            if 'captcha' not in session or user_captcha != session['captcha']['text']:
                # print(user_captcha, session['captcha'])
                # print(type(user_captcha), type(session['captcha']))
                flash('验证码错误')
                return redirect(url_for('register'))
            # 清除验证码session
            session.pop('captcha', None)

            users = load_users()

            if any(u['username'] == username for u in users):
                flash('用户名已存在')
                return redirect(url_for('register'))

            if any(u['email'] == email for u in users):
                flash('邮箱已被注册')
                return redirect(url_for('register'))

            # Create new user
            new_user = {
                "id": get_next_user_id(),
                "username": username,
                "email": email,
                "password": generate_password_hash(password),
                "registration_time": datetime.utcnow().isoformat() + 'Z',
                "last_login_time": datetime.utcnow().isoformat() + 'Z',
                "account_updated_time": datetime.utcnow().isoformat() + 'Z',
                "email_verified_time": None,
                "last_activity_time": datetime.utcnow().isoformat() + 'Z',
                "role": "user",
                "data_add_records": {}  # 新增默认字段
            }

            users.append(new_user)
            save_users(users)

            # Log the user in
            user_obj = User(new_user)
            login_user(user_obj)

            return redirect(url_for('index'))

        return render_template('register.html')
    except Exception as e:
        logger.error(f'注册处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 获取省份数据
def get_provinces():
    file_path = os.path.join('data', 'provinces.json')
    with open(file_path, 'r', encoding='utf-8') as f:
        provinces = json.load(f)
    return provinces


# 获取难度数据
def get_level():
    file_path = os.path.join('data', 'hikingLevel.json')
    with open(file_path, 'r', encoding='utf-8') as f:
        levels = json.load(f)
    return levels


# 首页
@app.route('/')
@login_required
@limiter.limit("5 per second")  # 限流处理
def index():
    try:
        # 更新访问统计并获取总访问量
        visit_count = update_visit_stats()

        # 获取注册用户数
        user_count = get_user_count()

        # 加载徒步路线数据
        hikes = load_data('hikes.json')

        provinces = get_provinces()
        levels = get_level()

        return render_template('index.html',
                               hikes=hikes,
                               user_count=user_count,
                               visit_count=visit_count,
                               provinces=provinces,
                               levels=levels)
    except Exception as e:
        logger.error(f'首页处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 路线详情页
@app.route('/hike/<int:hike_id>')
@login_required
@limiter.limit("5 per second")  # 限流处理
def hike_detail(hike_id):
    try:
        hikes = load_data('hikes.json')
        comments = load_data('comments.json')

        hike = next((h for h in hikes if h['id'] == hike_id), None)
        if not hike:
            return "路线不存在", 404

        # 筛选对应评论并按时间倒序
        hike_comments = [c for c in comments if c['hike_id'] == hike_id]
        hike_comments.sort(key=lambda x: x['timestamp'], reverse=True)

        return render_template('hike_detail.html', hike=hike, comments=hike_comments)
    except Exception as e:
        logger.error(f'路线详情页-处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 提交评论
@app.route('/hike/<int:hike_id>/comment', methods=['POST'])
@login_required
@limiter.limit("5 per second")  # 限流处理
def add_comment(hike_id):
    try:
        user = request.form.get('user')
        content = request.form.get('content')

        if not user or not content:
            return "请填写完整信息", 400

        new_comment = {
            "hike_id": hike_id,
            "user": user,
            "content": content,
            "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M")
        }
        comments = load_data('comments.json')
        comments.append(new_comment)
        save_data(comments, 'comments.json')

        return redirect(url_for('hike_detail', hike_id=hike_id))
    except Exception as e:
        logger.error(f'提交评论-处理请求失败: {str(e)}', exc_info=True)
        return 'Error', 500


# 添加徒步路线
@app.route('/add_hike', methods=['POST'])
@login_required
@limiter.limit("5 per second")  # 限流处理
def add_hike():
    try:
        # if current_user.role != 'admin':
        #     return "无权访问", 403

        # 检查是否有文件上传
        if 'image' not in request.files:
            return jsonify({'error': 'No image file uploaded'}), 400

        # 获取上传的图片文件
        image_file = request.files['image']

        # 如果用户没有选择文件，浏览器会提交一个空文件
        if image_file.filename == '':
            return jsonify({'error': 'No selected image file'}), 400

        # 生成唯一的文件名
        filename = str(uuid.uuid4()) + os.path.splitext(image_file.filename)[1]
        file_path = os.path.join(UPLOAD_FOLDER, filename)

        # 保存图片文件
        image_file.save(file_path)

        # 处理equipment数据，转换为数组格式
        equipment_str = request.form.get('equipment', '')
        # 假设装备项以换行符或逗号分隔，这里处理成数组
        equipment_list = [item.strip() for item in equipment_str.split('\n') if item.strip()]
        # 如果没有换行符，尝试按逗号分隔
        if not equipment_list:
            equipment_list = [item.strip() for item in equipment_str.split(',') if item.strip()]
        # 如果还是没有分隔符，就作为单一项处理
        if not equipment_list and equipment_str:
            equipment_list = [equipment_str]

        # 获取其他表单数据
        new_hike = {
            'name': request.form.get('name'),
            'image': filename,  # 保存文件名
            'description': request.form.get('description'),
            'address': request.form.get('address'),
            'route': request.form.get('route'),
            'duration': request.form.get('duration'),
            'length': request.form.get('length'),
            'best_time': request.form.get('best_time'),
            'difficulty': request.form.get('difficulty'),
            'score': float(request.form.get('score')),
            'equipment': equipment_list,  # 使用处理后的数组
            'two_step_map_url': request.form.get('two_step_map_url')
        }

        # 验证数据是否完整
        required_fields = ['name', 'description', 'address', "route", 'duration', 'length',
                           'best_time', 'difficulty', 'score', 'equipment', 'two_step_map_url']
        if not all(field in new_hike for field in required_fields):
            return jsonify({'error': 'Missing required fields'}), 400

        # 检查名称是否已存在
        hikes = load_data("hikes.json")
        name = new_hike['name'].strip().lower()  # 去除前后空格并转为小写比较
        if any(h['name'].strip().lower() == name for h in hikes):
            return jsonify({'error': '徒步路线已存在'}), 400

        # 生成新的ID
        new_hike['id'] = len(hikes) + 1

        # 添加新的徒步路线
        hikes.append(new_hike)

        # 保存更新后的数据到hikes.json
        save_data(hikes, "hikes.json")

        # 记录用户添加数据
        current_user.record_data_add()

        # 返回成功响应
        return jsonify({'message': 'Hike added successfully', 'hike': new_hike}), 201
    except Exception as e:
        logger.error(f'添加徒步路线-处理请求失败: {str(e)}', exc_info=True)
        return jsonify({'error': 'Internal server error'}), 500


# 添加徒步路线-条数限制
@app.route('/check_add_limit', methods=['GET'])
@login_required
def check_add_limit():
    try:
        if current_user.role == 'admin':
            return jsonify({
                'success': True,
                'can_add': True,
                'message': '管理员无限制',
                'remaining': '无限制'
            })

        today_count = current_user.get_today_add_count()
        can_add = today_count < 5
        remaining = 5 - today_count

        return jsonify({
            'success': True,
            'can_add': can_add,
            'message': f'今日已添加 {today_count} 条，还可添加 {remaining} 条' if can_add else '今日已达到添加限制',
            'remaining': remaining
        })
    except Exception as e:
        logger.error(f'检查添加限制失败: {str(e)}', exc_info=True)
        return jsonify({
            'success': False,
            'message': '检查添加限制时出错'
        }), 500


# 地区、难度、徒步名称 筛选(旧版)
# @app.route('/api/hikes', methods=['GET'])
# @login_required
# @limiter.limit("5 per second")  # 限流处理
# def filter_hikes():
#     try:
#         # 获取筛选参数
#         region = request.args.get('region', '全部地区')
#         difficulty = request.args.get('difficulty', '全部难度')
#         search = request.args.get('search', '').strip()  # 新增搜索参数
#
#         filtered_hikes = []
#         hikes_data = load_data('hikes.json')
#
#         for hike in hikes_data:
#             # 地区筛选
#             region_match = (region == '全部地区') or (region in hike.get('address', ''))
#
#             # 难度筛选
#             difficulty_match = (difficulty == '全部难度') or (difficulty == hike.get('difficulty', ''))
#
#             # 名称搜索 (新增)
#             name_match = True
#             if search:
#                 name = hike.get('name', '').lower()
#                 name_match = search.lower() in name
#
#             if region_match and difficulty_match and name_match:
#                 filtered_hikes.append(hike)
#
#         return jsonify({
#             'success': True,
#             'data': filtered_hikes,
#             'count': len(filtered_hikes)
#         })
#     except Exception as e:
#         logger.error(f'筛选-处理请求失败: {str(e)}', exc_info=True)
#         return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/hikes', methods=['GET'])
@login_required
@limiter.limit("5 per second")  # 限流处理
def filter_hikes():
    try:
        # 获取筛选参数
        province = request.args.get('province', '')  # 省份参数
        level = request.args.get('level', '')        # 难度参数
        search = request.args.get('search', '').strip()  # 搜索参数

        filtered_hikes = []
        hikes_data = load_data('hikes.json')

        for hike in hikes_data:
            # 省份筛选
            province_match = not province or (province in hike.get('address', ''))

            # 难度筛选
            level_match = not level or (level == hike.get('difficulty', ''))

            # 名称搜索
            name_match = True
            if search:
                name = hike.get('name', '').lower()
                name_match = search.lower() in name

            if province_match and level_match and name_match:
                filtered_hikes.append(hike)

        return jsonify({
            'success': True,
            'data': filtered_hikes,
            'count': len(filtered_hikes)
        })
    except Exception as e:
        logger.error(f'筛选-处理请求失败: {str(e)}', exc_info=True)
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/profile', methods=['GET', 'POST'])
@limiter.limit("5 per second")
@login_required
def profile():
    if request.method == 'POST':
        # 获取表单数据
        username = request.form.get('username')
        email = request.form.get('email')
        password = request.form.get('password')
        confirm_password = request.form.get('confirm_password')

        # 加载所有用户数据
        users = load_users()
        user_index = None
        current_user_data = None

        # 查找当前用户
        for i, user in enumerate(users):
            if user['id'] == current_user.id:
                user_index = i
                current_user_data = user
                break

        if current_user_data is None:
            flash('用户数据不存在', 'error')
            return redirect(url_for('profile'))

        # 验证并更新用户信息
        updated = False

        if username and username != current_user_data['username']:
            # 检查用户名是否已存在
            if any(u['username'] == username for u in users if u['id'] != current_user.id):
                flash('该用户名已被使用', 'error')
            else:
                current_user_data['username'] = username
                updated = True

        if email and email != current_user_data['email']:
            # 检查邮箱是否已存在
            if any(u['email'] == email for u in users if u['id'] != current_user.id):
                flash('该邮箱已被使用', 'error')
            else:
                current_user_data['email'] = email
                updated = True

        if password and confirm_password:
            if password != confirm_password:
                flash('两次输入的密码不一致', 'error')
            else:
                current_user_data['password'] = generate_password_hash(password)
                updated = True

        if updated:
            users[user_index] = current_user_data
            try:
                save_users(users)
                flash('个人信息更新成功', 'success')
            except Exception as e:
                logger.error(f"保存用户数据失败: {e}")
                flash('保存失败，请稍后再试', 'error')

        return redirect(url_for('profile'))

    # GET请求时加载用户数据
    users = load_users()
    current_user_data = next((user for user in users if user['id'] == current_user.id), None)

    if current_user_data is None:
        flash('无法加载用户数据', 'error')
        return redirect(url_for('index'))

    return render_template('user_profile.html', user=current_user_data)


# 留言数据存储和加载函数
def load_messages():
    try:
        with open('data/messages.json', 'r', encoding='utf-8') as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return []


def save_messages(messages):
    os.makedirs('data', exist_ok=True)
    with open('data/messages.json', 'w', encoding='utf-8') as f:
        json.dump(messages, f, ensure_ascii=False, indent=2)


# 留言板路由
@app.route('/message-board', methods=['GET', 'POST'])
@limiter.limit("5 per second")
@login_required
def message_board():
    messages = load_messages()

    if request.method == 'POST':
        content = request.form.get('content', '').strip()

        if not content:
            flash('留言内容不能为空', 'error')
        elif len(content) > 500:
            flash('留言内容不能超过500字', 'error')
        else:
            # 创建新留言
            new_message = {
                'id': str(uuid.uuid4()),
                'user_id': current_user.id,
                'username': current_user.username,
                'content': content,
                'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
                'replies': []
            }

            messages.insert(0, new_message)  # 新留言放在最前面

            try:
                save_messages(messages)
                flash('留言发布成功', 'success')
                return redirect(url_for('message_board'))
            except Exception as e:
                logger.error(f"保存留言失败: {e}")
                flash('留言发布失败，请稍后再试', 'error')

    # 按时间倒序排列留言
    sorted_messages = sorted(messages, key=lambda x: x['created_at'], reverse=True)
    return render_template('message_board.html', messages=sorted_messages)


# 删除留言路由
@app.route('/delete-message/<message_id>', methods=['POST'])
@limiter.limit("5 per second")
@login_required
def delete_message(message_id):
    messages = load_messages()

    # 查找要删除的留言
    message_to_delete = next((msg for msg in messages if msg['id'] == message_id), None)

    if not message_to_delete:
        flash('留言不存在', 'error')
    elif message_to_delete['user_id'] != current_user.id:
        flash('您只能删除自己的留言', 'error')
    else:
        try:
            # 删除留言
            messages = [msg for msg in messages if msg['id'] != message_id]
            save_messages(messages)
            flash('留言已删除', 'success')
        except Exception as e:
            logger.error(f"删除留言失败: {e}")
            flash('删除失败，请稍后再试', 'error')

    return redirect(url_for('message_board'))


# 回复留言路由
@app.route('/reply-message/<message_id>', methods=['POST'])
@limiter.limit("5 per second")
@login_required
def reply_message(message_id):
    messages = load_messages()
    content = request.form.get('content', '').strip()

    if not content:
        flash('回复内容不能为空', 'error')
    elif len(content) > 300:
        flash('回复内容不能超过300字', 'error')
    else:
        # 查找原始留言
        original_message = next((msg for msg in messages if msg['id'] == message_id), None)

        if not original_message:
            flash('原始留言不存在', 'error')
        else:
            # 添加回复
            new_reply = {
                'id': str(uuid.uuid4()),
                'user_id': current_user.id,
                'username': current_user.username,
                'content': content,
                'created_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            }

            original_message['replies'].insert(0, new_reply)

            try:
                save_messages(messages)
                flash('回复成功', 'success')
            except Exception as e:
                logger.error(f"保存回复失败: {e}")
                flash('回复失败，请稍后再试', 'error')

    return redirect(url_for('message_board'))


if __name__ == '__main__':
    app.run(host="0.0.0.0", port=5001, debug=True)